﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Flurl.CodeGen
{
	class Program
	{
		static int Main(string[] args) {
			var codeRoot = (args.Length > 0) ? args[0] : @"..\..\..\..\..";

			try {
				var path = codeRoot + @"\src\Flurl\GeneratedExtensions.cs";
				if (!File.Exists(path)) {
					ShowError("Code file not found: " + Path.GetFullPath(path));
					return 2;
				}

				File.WriteAllText(path, "");
				using (var writer = new CodeWriter(path)) {
					writer
						.WriteLine("// This file was auto-generated by Flurl.CodeGen. Do not edit directly.")
						.WriteLine("using System;")
						.WriteLine("using System.Collections.Generic;")
						.WriteLine("")
						.WriteLine("namespace Flurl")
						.WriteLine("{")
						.WriteLine("/// <summary>")
						.WriteLine("/// Fluent URL-building extension methods on String and Uri.")
						.WriteLine("/// </summary>")
						.WriteLine("public static class GeneratedExtensions")
						.WriteLine("{");

					WriteUrlBuilderExtensionMethods(writer);

					writer
						.WriteLine("}")
						.WriteLine("}");
				}

				path = codeRoot + @"\src\Flurl.Http\GeneratedExtensions.cs";
				if (!File.Exists(path)) {
					ShowError("Code file not found: " + Path.GetFullPath(path));
					return 2;
				}

				File.WriteAllText(path, "");
				using (var writer = new CodeWriter(path)) {
					writer
						.WriteLine("// This file was auto-generated by Flurl.CodeGen. Do not edit directly.")
						.WriteLine("using System;")
						.WriteLine("using System.Collections.Generic;")
						.WriteLine("using System.IO;")
						.WriteLine("using System.Net;")
						.WriteLine("using System.Net.Http;")
						.WriteLine("using System.Threading;")
						.WriteLine("using System.Threading.Tasks;")
						.WriteLine("using Flurl.Http.Configuration;")
						.WriteLine("using Flurl.Http.Content;")
						.WriteLine("")
						.WriteLine("namespace Flurl.Http")
						.WriteLine("{")
						.WriteLine("/// <summary>")
						.WriteLine("/// Fluent extension methods on String, Url, Uri, and IFlurlRequest.")
						.WriteLine("/// </summary>")
						.WriteLine("public static class GeneratedExtensions")
						.WriteLine("{");

					WriteHttpExtensionMethods(writer);

					writer
						.WriteLine("}")
						.WriteLine("}");
				}

				Console.WriteLine("File writing succeeded.");
				return 0;
			}
			catch (Exception ex) {
				ShowError(ex.ToString());
				return 2;
			}
		}

		private static void ShowError(string error) {
			Console.ForegroundColor = ConsoleColor.Red;
			Console.WriteLine(error);
			Console.ReadLine();
		}

		private static MethodArg[] _extendedArgs = new[] {
			new MethodArg { Name = "request", Type = "IFlurlRequest", Description = "This IFlurlRequest" },
			new MethodArg { Name = "url", Type = "Url", Description = "This Flurl.Url." },
			new MethodArg { Name = "url", Type = "string", Description = "This URL." },
			new MethodArg { Name = "uri", Type = "Uri", Description = "This System.Uri." }
		};

		private static void WriteUrlBuilderExtensionMethods(CodeWriter writer) {
			foreach (var xarg in _extendedArgs.Skip(2)) { // skip 2 because we only auto-gen for string and Uri
				foreach (var xm in Metadata.GetUrlReturningExtensions(xarg)) {
					Console.WriteLine($"writing {xm.Name} for {xarg.Type}...");
					xm.Write(writer, $"new Url({xarg.Name})");
				}
			}
		}

		private static void WriteHttpExtensionMethods(CodeWriter writer) {
			var reqArg = _extendedArgs[0];

			foreach (var xm in Metadata.GetHttpCallingExtensions(reqArg)) {
				Console.WriteLine($"writing {xm.Name} for IFlurlRequest...");
				xm.Write(writer, () => {
					var args = new List<string>();
					var genericArg = xm.IsGeneric ? "<T>" : "";

					args.Add(
						xm.HttpVerb == null ? "verb" :
						xm.HttpVerb == "Patch" ? "new HttpMethod(\"PATCH\")" : // there's no HttpMethod.Patch
						"HttpMethod." + xm.HttpVerb);

					if (xm.HasRequestBody)
						args.Add("content: content");

					args.Add("cancellationToken: cancellationToken");
					args.Add("completionOption: completionOption");

					if (xm.RequestBodyType != null) {
						writer.WriteLine("var content = new Captured@0Content(@1);",
							xm.RequestBodyType,
							xm.RequestBodyType == "String" ? "data" : $"request.Settings.{xm.RequestBodyType}Serializer.Serialize(data)");
					}

					var receive = (xm.ResponseBodyType != null) ? $".Receive{xm.ResponseBodyType}{genericArg}()" : "";
					writer.WriteLine($"return {reqArg.Name}.SendAsync({string.Join(", ", args)}){receive};");
				});
			}

			foreach (var xarg in _extendedArgs.Skip(1)) { // skip 1 because these don't apply to IFlurlRequest
				foreach (var xm in Metadata.GetHttpCallingExtensions(xarg).Concat(Metadata.GetRequestReturningExtensions(xarg))) {
					Console.WriteLine($"writing {xm.Name} for {xarg.Type}...");
					xm.Write(writer, $"new FlurlRequest({xarg.Name})");
				}
			}
		}
	}
}